package com.pointi.emoClientM;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

import com.pointi.emoTools.EmotionPacket;
import com.pointi.emoTools.WorkList;
import com.pointi.emoTools.emoCheckList;
import com.pointi.emoTools.emoDefine;
import com.pointi.emoTools.emoLogManager;

public class ThreadTCPClient extends Thread
{
	private ConcurrentLinkedQueue<ThreadTCPClient> _TCPClient ;
	private JDBC jdbc ;
	private WorkList work ;
	private ArrayList<String> RCL ;
	private Socket mSocket ;

	private final Object lock = new Object() ;
	private static int threadSeq = 0 ;
	private int TID = 0 ;

	public ThreadTCPClient(ConcurrentLinkedQueue<ThreadTCPClient> inTCPClient)
	{
		_TCPClient = inTCPClient ;
		synchronized(lock)
		{
			threadSeq ++ ;
			TID = threadSeq ;
		}
	}

	public void start(JDBC jdbc, WorkList work, ArrayList<String> RCL)
	{
		this.jdbc = jdbc ;
		this.work = work ;
		this.RCL = RCL ;
		_TCPClient.add(this) ;
		super.start() ;
	}

	public void run()
	{
		try
		{
			// ____________________________________________________________________________________________________
			// Get TCPClient Destination

			mSocket = null ;
			BufferedInputStream bis = null ;
			BufferedOutputStream bos = null ;

			jdbc.fnGetServerInfo(work) ;
			try
			{
				mSocket = new Socket(work.fnGetServerAddress(), work.fnGetServerPort()) ;
				System.out.println("[EM][CRT][" + TID + "] Connection Accept : " + mSocket.getInetAddress() + ":" + mSocket.getPort()) ;
				jdbc.fnGetFileInfo(work) ;
				this.fnPrintConnectionInfo() ;
			}
			catch (Exception e)
			{
				new emoLogManager().printTXT("", "", "", work._CallKey + " : Socket Connection Error") ;
				e.printStackTrace() ;
			}

			// ____________________________________________________________________________________________________
			// Read PCM Data

			File mFile = null ;
			AudioInputStream ais = null ;
			int mFileLength = 0 ;
			byte [] mPCM = null ;
			try
			{
				mFile = new File(work.fnGetFileInfo()) ;
				if (mFile.isFile()) // is File Exist ?
				{
					jdbc.fnUpdateCheckList(emoCheckList.mCheckClient, emoCheckList._START, Integer.valueOf(work._CallKey)) ;
					ais = AudioSystem.getAudioInputStream(mFile) ;
					mFileLength = (int)ais.getFrameLength() * 2 ;
				}
			}
			catch(Exception e)
			{
				new emoLogManager().printTXT("", "", "", work._CallKey + " : File Open Error") ;
				e.printStackTrace() ;
			}

			// ____________________________________________________________________________________________________
			// Convert PCM to Byte Array

			if (mFileLength > 0)
			{
				mPCM = new byte [mFileLength] ;
				try
				{
					ais.read(mPCM, 0, mPCM.length) ;
				}
				catch(Exception e)
				{
					new emoLogManager().printTXT("", "", "", work._CallKey + " : File Read Error") ;
					e.printStackTrace() ;
				}
			}

			// ____________________________________________________________________________________________________
			// Transportation
			//		-	PCM 	: Original PCM Data (byte)
			//		-	Buffer	: Part of WAV (byte)
			//		-	Frame	: Converted Buffer (short)
			//		-	Packet	: Data for Transportation (Header + Frame)

			int mPacketSize = EmotionPacket.packetSize ;
			int mFrameSize = EmotionPacket.frameSize ;
			int mBufferSize = mFrameSize * 2 ;
			int SP = 0 ;
			int CP = mBufferSize ; // Byte Array Length = (Short Array Length * 2)
			int EP = mFileLength ;
			int _PacketType = 0 ;

			bos = new BufferedOutputStream(mSocket.getOutputStream()) ;
			bis = new BufferedInputStream(mSocket.getInputStream()) ;
			emoDefine mDefine = new emoDefine() ;

			boolean loopHandler = true ;
			boolean isSTART = true ;
			boolean isON = false ;

			while(loopHandler)
			{
				// ____________________________________________________________________________________________________
				// Check Process

				if (isSTART)
				{
					isSTART = false ;
					isON = true ;
					_PacketType = EmotionPacket._FIRST_PACKET ;
					// jdbc.fnUpdateCheckList(emoCheckList.mCheckClient, emoCheckList._START, Integer.valueOf(work._CallKey)) ;
				}
				else if (isON)
				{
					isON = false ;
					_PacketType = EmotionPacket._MIDDLE_PACKET ;
				}
				
				// ____________________________________________________________________________________________________
				// Make Voice Frame

				// Copy Part of WAV Data
				byte [] mBuffer = new byte [mBufferSize] ;
				System.arraycopy(mPCM, SP, mBuffer, 0, CP) ;
				if ((SP + CP) >= mFileLength) // [TRUE] Last Frame
				{
					_PacketType = EmotionPacket._LAST_PACKET ;
					if (CP < mBufferSize)
						for (int i=CP; i<mBufferSize; i++) { mBuffer[i] = 0 ; } // Zero-Padding
				}
				SP += CP ; if ((SP + CP) > EP) CP = EP - SP ; // Next Frame Index
				short [] mFrame = mDefine.byte2short(mBuffer) ; // Convert Buffer to Frame | byte to short

				// ____________________________________________________________________________________________________
				// Send Voice Frame to EmotionServer

				int mSide = 1 ;
				EmotionPacket sendPacket = new EmotionPacket(_PacketType, TID, work._CallKey, mSide, mFrame) ;
				byte [] sendBuffer = new byte [mPacketSize] ;
				try
				{
					ByteArrayOutputStream baos = new ByteArrayOutputStream(mPacketSize) ;
					ObjectOutput out = null ;
					out = new ObjectOutputStream(baos) ;
					out.writeObject(sendPacket) ;
					sendBuffer = baos.toByteArray() ;
				}
				catch (IOException e)
				{
					new emoLogManager().printTXT("", "", "", work._CallKey + " : Output Stream Error") ;
					e.printStackTrace();
				}
				catch (Exception e) { e.printStackTrace() ; }
				bos.write(sendBuffer) ;
				bos.flush() ;

				// ____________________________________________________________________________________________________
				// Receive Acknowledge from EmotionServer

				bis.read(new byte[1]) ;
				
				// ____________________________________________________________________________________________________
				// Analysis Done
				// 		-	Must be Checked Thread ID, for Closed own.
				//		-	Update t_checklist_yyyymm.check_clietn = 3
				
				if (_PacketType == EmotionPacket._LAST_PACKET)
				{
					jdbc.fnUpdateCheckList(emoCheckList.mCheckClient, emoCheckList._END, Integer.valueOf(work._CallKey)) ;
					jdbc.fnDeleteWorkFromScheduler(work._Seq) ;
					break ;
				}
			}
			bis.close() ;
			bos.close() ;
			mSocket.close() ;
			RCL.remove(work._CallKey) ;
		}
		catch(Exception e)
		{
			new emoLogManager().printTXT("", "", "", work._CallKey + " : Client Error") ;
			e.printStackTrace() ;
		}
		finally
		{
			try { mSocket.close() ; }
			catch(Exception e) { e.printStackTrace() ; }
		}
		emoLogManager.mLog("[EM][CRT] TCPServer Thread | Removed");
		_TCPClient.remove(this) ;
	}

	// ____________________________________________________________________________________________________

	private void fnPrintConnectionInfo()
	{
		String msg = "" ;
		msg = "[EM][CRT][" + TID + "] Connect Info : " + work._CallKey + ", " + work._FilePath + work._FileName ;
		System.out.println(msg) ;
	}
}
